package com.doctcloud.datacloud.service;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.doctcloud.datacloud.entity.OutpBillItemsEntity;
import com.doctcloud.datacloud.mapper.OutpBillItemsEntityMapper;
import com.doctcloud.hisinfo.api.RemoteHisOutpBillService;
import com.doctcloud.hisinfo.histable.domain.outpbill.OutpBillItemsDo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class OutpBillItemsEntityService extends ServiceImpl<OutpBillItemsEntityMapper, OutpBillItemsEntity> {

    @Value("${sync.batch.size:500}")
    private int BATCH_SIZE;

    private final RemoteHisOutpBillService remoteHisOutpBillService;

    /**
     * 同步门诊收费开单记录。
     *
     * @param rcptNo 收费单据号
     */
    @Transactional(rollbackFor = Exception.class)
    public void syncRcpt(String rcptNo) {
        // 1. 参数校验：避免空指针
        if (rcptNo == null || rcptNo.trim().isEmpty()) {
            log.error("同步门诊明细记录失败：参数 rcptNo 为空");
            throw new IllegalArgumentException("同步失败：收费单据号（rcptNo）不能为空");
        }

        log.info("--- 开始同步门诊收费明细记录：rcptNo={} ---", rcptNo);
        long startTime = System.currentTimeMillis();

        try {
            // 2. 从远程服务获取数据
            List<OutpBillItemsDo> remoteOutpbillLists = remoteHisOutpBillService.listBillByRcpt(rcptNo);
            if (CollectionUtils.isEmpty(remoteOutpbillLists)) {
                log.warn("门诊收费明细记录同步完成：rcptNo={}，远程服务无返回数据", rcptNo);
                return;
            }
            log.info("从远程服务获取门诊明细记录数：rcptNo={}，数量={}", rcptNo, remoteOutpbillLists.size());

            // 3. DO 转 Entity + 过滤无效数据
            List<OutpBillItemsEntity> entitiesToUpsert = remoteOutpbillLists.stream()
                    .filter(this::isValidOutpBillDo)
                    .map(this::convertDoToEntity)
                    .collect(Collectors.toList());

            if (CollectionUtils.isEmpty(entitiesToUpsert)) {
                log.info("门诊收费明细记录同步完成：rcptNo={}，无有效数据（已过滤空字段记录）", rcptNo);
                return;
            }
            log.info("待同步门诊收费明细数：rcptNo={}，有效数量={}，分批大小：{}", rcptNo, entitiesToUpsert.size(), BATCH_SIZE);

            // 4. 执行分批批量 UPSERT
            int totalAffectedRows = 0;
            int totalBatches = (int) Math.ceil((double) entitiesToUpsert.size() / BATCH_SIZE);

            for (int i = 0; i < totalBatches; i++) {
                int startIndex = i * BATCH_SIZE;
                int endIndex = Math.min(startIndex + BATCH_SIZE, entitiesToUpsert.size());
                List<OutpBillItemsEntity> batchEntities = entitiesToUpsert.subList(startIndex, endIndex);

                log.debug("正在执行第 {} 批/共 {} 批，处理 {} 条记录。", i + 1, totalBatches, batchEntities.size());
                int affectedRows = baseMapper.batchUpsert(batchEntities);
                totalAffectedRows += affectedRows;
            }

            // 5. 同步完成日志
            log.info("--- 门诊收费明细记录同步成功：rcptNo={}，总耗时={}ms，数据库影响总行数={} ---",
                    rcptNo, System.currentTimeMillis() - startTime, totalAffectedRows);

        } catch (IllegalArgumentException e) {
            // 捕获参数校验等主动抛出的异常，无需打印堆栈
            log.error("同步门诊收费明细失败：rcptNo={}，原因={}", rcptNo, e.getMessage());
            throw e; // 向上抛出，让调用方处理
        } catch (Exception e) {
            // 捕获其他未知异常，打印完整堆栈，便于排查
            log.error("同步门诊收费明细记录异常：rcptNo={}，原因=", rcptNo, e);
            // 此处可添加告警逻辑
            throw new RuntimeException("同步门诊收费明细记录失败：" + e.getMessage(), e);
        }
    }

    /**
     * 通用过滤：判断 OutpBillItemsDo 是否为有效数据。
     */
    private boolean isValidOutpBillDo(OutpBillItemsDo doObj) {
        if (doObj == null) {
            return false;
        }
        // 关键字段非空校验
        return isNotEmpty(doObj.getItemCode()) &&
                isNotEmpty(doObj.getItemClass()) &&
                isNotEmpty(doObj.getItemSpec()) &&
                isNotEmpty(doObj.getRcptNo()) &&
                isNotEmptyAndNotZero(doObj.getAmount());
    }

    /**
     * 统一转换：OutpBillItemsDo -> OutpBillItemsEntity。
     */
    private OutpBillItemsEntity convertDoToEntity(OutpBillItemsDo doObj) {
        OutpBillItemsEntity entity = new OutpBillItemsEntity();
        org.springframework.beans.BeanUtils.copyProperties(doObj, entity);
        return entity;
    }

    /**
     * 通用判空：判断字符串是否非空。
     */
    private boolean isNotEmpty(String str) {
        return str != null && !str.trim().isEmpty();
    }

    /**
     * 通用判空：判断 Integer 数字是否非空且不为 0。
     */
    private boolean isNotEmptyAndNotZero(Integer number) {
        return number != null && number != 0;
    }
}